package com.googlecode.dgwt.client.dijit;

import java.util.Collections;
import java.util.Set;

import com.google.gwt.dom.client.Element;
import com.googlecode.dgwt.client.dijit.base.WidgetProperty;
import com.googlecode.dgwt.client.dijit.base.widget.ViewList;
import com.googlecode.dgwt.client.dijit.base.widget.WidgetBase;
import com.googlecode.dgwt.client.dijit.base.widget.WidgetEngine;
import com.googlecode.dgwt.client.dijit.theme.CSS;
import com.googlecode.dgwt.client.dijit.theme.Theme;
import com.googlecode.dgwt.client.dojo.Dojo;
import com.googlecode.dgwt.client.dojo.core.DOMElement;

/**
 * Base class for all {@link Dijit} Widgets.
 * 
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public class Widget extends WidgetBase {

    
    /**
     * Constant to identify a getter/setter for DOM element properties
     */
    protected static final WidgetProperty DOM_PROPERTY = WidgetProperty.DOM_PROPERTY;
    
    /**
     * Constant to identify a getter/setter for style properties
     */
    protected static final WidgetProperty STYLE_PROPERTY = WidgetProperty.STYLE_PROPERTY;
    
    /**
     * Constant to identify a getter/setter for element attributes
     */
    protected static final WidgetProperty ATTRIBUTE_PROPERTY = WidgetProperty.ATTRIBUTE_PROPERTY;

    /**
     * List of views owned by this Widget.
     */
    private ViewList viewList;
    
    
    /**
     * Creates a new empty Widget. <br/>
     * <br/>
     * Note that {@link #buildRendering()} is responsible to create
     * {@link #domElement} and any other child nodes. If <code>ready</code>
     * parameter be passed as False, {@link #postCreate()} should be called
     * before the constructor of descendant classes finish.
     * 
     * @param ready
     *            if True, {@link #postCreate()} will be called immediately
     */
    protected Widget(boolean ready){
        Dijit.widgetList.add(this);
        getEngine().initialize();
        if (ready) {
            postCreate();            
        }
    }

    /**
     * Creates a new Widget.
     * 
     * @param element
     *            the element to be used as {@link #domElement}
     */
    protected Widget(Element element) {
        this(false);
        setDomElement(element);
        postCreate();
    }
    
    
    /**
     * Called by {@link Dijit} after it loads.
     */
    final void startup() {
        getEngine().startup();
    }
    
    protected void setProperty(WidgetProperty propertyType, String propertyName, Object propertyValue){
        propertyType.set(this, propertyName, propertyValue);
    }

    protected Object getProperty(WidgetProperty propertyType, String propertyName){
        return propertyType.get(this, propertyName);
    }
    
    /**
     * Gets an attribute of this Widget. e.g: <code>getAttribute("id")</code>
     * will return the HTML id for this Widget
     * 
     * @param name
     *            the attribute name
     * @return the attribute value
     */
    public final String getAttribute(String name){
        Object value = getProperty(ATTRIBUTE_PROPERTY, name);
        return value == null ? "" : "" + value;
    }
            
    /**
     * Sets an attribute on this Widget. e.g:
     * <code>setAttribute("id", "myId")</code> will define the HTML id for this
     * Widget
     * 
     * @param name
     *            the attribute name
     * @param value
     *            the attribute value
     */
    public final void setAttribute(String name, String value){
        setProperty(ATTRIBUTE_PROPERTY, name, value);
    }

    /**
     * Gets a style property of this Widget. e.g:
     * <code>getStyleProperty("color")</code> will return the current computed
     * font color for this Widget.
     * 
     * @param name
     *            the style property name
     *            
     * @see Dojo#getStyle(com.google.gwt.dom.client.Node, String)
     * 
     * @return the current computed style value for this Widget
     */
    public final String getStyleProperty(String name){
        Object value = getProperty(STYLE_PROPERTY, name);
        return value == null ? "" : "" + value;
    }

    /**
     * Sets a style property on this Widget. e.g:
     * <code>setStyleProperty("color", "#000")</code> will define the font color.
     * as black.
     * 
     * @param name
     *            the style property name
     * @param value
     *            the value to set
     */
    public void setStyleProperty(String name, String value){
        setProperty(STYLE_PROPERTY, name, value);
    }

    /**
     * Sets a style property on this Widget. e.g:
     * <code>setStyleProperty("width", 200)</code> will define the width to
     * 200px.
     * 
     * @param name
     *            the style property name
     * @param value
     *            the value in pixels to set
     */
    public void setStylePropertyPx(String name, int value){
        setStyleProperty(name, value + "px");
    }
        
    /**
     * Gets the CSS declaration used by this Widget.
     * 
     * @see CSS
     * 
     * @return the CSS.
     */
    public CSS getThemeCSS(){
        return Dijit.getTheme().dijitWidget();
    }

    /**
     * Event that occurs before the current {@link Theme} be changed.
     * @param newTheme new {@link Theme} that will be used
     */
    protected void onThemeChanging(Theme newTheme) {
        getEngine().onThemeChanging(newTheme);
    }
    
    /**
     * Event that occurs after the current {@link Theme} be changed.
     * @param oldTheme previous {@link Theme} used
     */
    protected void onThemeChanged(Theme oldTheme) {
        getEngine().onThemeChanged(oldTheme);
    }

    /**
     * Sets the {@link #domElement} that is the root element for this Widget. <br/>
     * Note that this method should not be called after calling
     * {@link #postCreate()}.
     * 
     * @param domElement
     *            the {@link Element} object
     */
    protected void setDomElement(Element domElement) {
        getEngine().setDomElement(domElement);
    }

    /**
     * Gets the element used as container when adding child Widgets to this.
     * 
     * @return the container {@link Element}
     */
    public Element getContainerElement(){
        return getAttachedElement(WidgetEngine.CONTAINER_ELEMENT);
    }

    /**
     * Sets the element to use as container when adding child Widgets to this.
     * 
     * @param element
     *            an {@link Element} object
     */
    protected void setContainerElement(Element element){
        setAttachedElement(WidgetEngine.CONTAINER_ELEMENT, element);
    }

    /**
     * Gets the element to use as handler of size properties like "width" and
     * "height".
     * 
     * @return the size able {@link Element}
     */
    public Element getSizeableElement() {
        return getAttachedElement(WidgetEngine.SIZEABLE_ELEMENT);
    }

    /**
     * Sets the element to use as handler of size properties like "width" and
     * "height".
     * 
     * @param element
     *            an {@link Element} object
     */
    protected void setSizeableElement(Element element){
        setAttachedElement(WidgetEngine.SIZEABLE_ELEMENT, element);
    }
    
    /* (non-Javadoc)
     * @see com.google.gwt.user.client.ui.UIObject#getStyleElement()
     */
    @Override
    public DOMElement getStyleElement() {
        return getAttachedElement(WidgetEngine.STYLE_ELEMENT).cast();
    }

    /**
     * Sets the element used to handle style properties.
     * 
     * @param element
     *            an {@link Element} object
     */
    protected void setStyleElement(Element element){
        setAttachedElement(WidgetEngine.STYLE_ELEMENT, element);
    }

    /**
     * Gets the HTML id for this Widget. <br/>
     * By default, an generated id is provided.
     * 
     * @see Element#getId()
     * 
     * @return the HTML id for this Widget
     */
    public final String getId(){
        return getAttribute("id");
    }

    /**
     * Sets the HTML id for this Widget.
     * 
     * @see Element#setId(String)
     * 
     * @param value
     *            the new id
     */
    public final void setId(String value){
        setAttribute("id", value);
    }

    /* (non-Javadoc)
     * @see com.google.gwt.user.client.ui.UIObject#setHeight(java.lang.String)
     */
    @Override
    public final void setHeight(String height) {
        setStyleProperty("height", height);
    }

    /* (non-Javadoc)
     * @see com.google.gwt.user.client.ui.UIObject#setWidth(java.lang.String)
     */
    @Override
    public final void setWidth(String width) {
        setStyleProperty("width", width);
    }
    
    /**
     * Sets the margin-bottom style property for this Widget.
     * 
     * @param value
     *            any valid CSS value like "10px", "4em", etc.
     */
    public void setMarginBottom(String value){
        setStyleProperty("marginBottom", value);
    }    
    
    /**
     * Sets the margin-left style property for this Widget.
     * 
     * @param value
     *            any valid CSS value like "10px", "4em", etc.
     */
    public void setMarginLeft(String value){
        setStyleProperty("marginLeft", value);
    }
    
    /**
     * Sets the margin-right style property for this Widget.
     * 
     * @param value
     *            any valid CSS value like "10px", "4em", etc.
     */
    public void setMarginRight(String value){
        setStyleProperty("marginRight", value);
    }
    
    /**
     * Sets the margin-top style property for this Widget.
     * 
     * @param value
     *            any valid CSS value like "10px", "4em", etc.
     */
    public void setMarginTop(String value){
        setStyleProperty("marginTop", value);
    }
    
    /**
     * Sets the margin-bottom style property for this Widget.
     * Note that this value will be handle in pixels.
     * 
     * @param value
     *            a value in pixels
     */
    public void setMarginBottom(int value){
        setMarginBottom(value + "px");
    }
    
    /**
     * Sets the margin-left style property for this Widget.
     * Note that this value will be handle in pixels.
     * 
     * @param value
     *            a value in pixels
     */
    public void setMarginLeft(int value){
        setMarginLeft(value + "px");
    }
    
    /**
     * Sets the margin-right style property for this Widget.
     * Note that this value will be handle in pixels.
     * 
     * @param value
     *            a value in pixels
     */
    public void setMarginRight(int value){
        setMarginRight(value + "px");
    }
        
    /**
     * Sets the margin-top style property for this Widget.
     * Note that this value will be handle in pixels.
     * 
     * @param value
     *            a value in pixels
     */
    public void setMarginTop(int value){
        setMarginTop(value + "px");
    }

    protected boolean addView(View view) {
        if (viewList == null){
            viewList = new ViewList(this);
        }
        return viewList.add(view);
    }

    protected boolean hasView(Object view) {
        return viewList != null && viewList.contains(view);
    }
    
    protected View findView(String name){
        if (viewList != null){
            return viewList.findView(name);
        }
        return null;
    }

    protected View findView(Widget widget){
        if (viewList != null){
            return viewList.findView(widget);
        }
        return null;
    }
    
    protected Set<View> getViews(){
        if (viewList != null){
            return viewList;
        }
        return Collections.emptySet();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj){
            return true;
        }
        if (obj instanceof View 
                && this == ((View) obj).getWidget()){
            return true;
        }
        return false;
    }
    
}
